// (C) 2021 Intel Corporation. All rights reserved.
// Your use of Intel Corporation's design tools, logic functions and other 
// software and tools, and its AMPP partner logic functions, and any output 
// files from any of the foregoing (including device programming or simulation 
// files), and any associated documentation or information are expressly subject 
// to the terms and conditions of the Intel Program License Subscription 
// Agreement, Intel FPGA IP License Agreement, or other applicable 
// license agreement, including, without limitation, that your use is for the 
// sole purpose of programming logic devices manufactured by Intel and sold by 
// Intel or its authorized distributors.  Please refer to the applicable 
// agreement for further details.

module espi_vuart_register
    #(
        parameter ESPI_SERIAL_MSIX_INDEX = 8'd0
    )
 (
    input                   clk,
    input                   reset_n,
    output reg  [ 31: 0]    readdata,
    output wire             readdatavalid,
    input       [  4: 0]    address,
    input                   chipselect,
    input                   write_n,
    input                   read_n,
    input       [ 31: 0]    writedata,

    output reg              vuart_avl_rbr_en,
    input       [  7: 0]    vuart_avl_rbr,
    output reg  [  7: 0]    vuart_avl_thr,
    output reg              vuart_avl_thr_en,
    input       [  7: 0]    vuart_rbr_fifo_usedw,
    input       [  7: 0]    vuart_thr_fifo_usedw,
    output reg  [ 15: 0]    vuart_avl_addr,

    // signals to espi for vuart interface
    input                   espi_vuart_rbrfifo_empty,
    input                   espi_vuart_thrfifo_empty_n,
    input wire    [7:0]     vuart_avl_dll,
    input wire    [7:0]     vuart_avl_dlh,
    input wire    [7:0]     vuart_avl_ier,
    input wire    [7:0]     vuart_avl_fcr,
    input wire    [7:0]     vuart_avl_mcr,
    input wire    [7:0]     vuart_avl_iir,
    input wire    [7:0]     vuart_avl_lcr,
    input wire    [7:0]     vuart_avl_lsr,
    input wire    [7:0]     vuart_avl_msr,

    // MSIX handshake
    output reg              espi_serial_msix_req,
    input                   espi_serial_msix_ack
);

wire            wr_strobe;
wire            rd_strobe;

reg    [7:0]    vuart_avl_scr = 8'h0;
wire   [31:0]   vuart_avl_fsr;
reg    [31:0]   isr;

reg             dlab;
wire   [7:0]    reg0_rd;
wire   [7:0]    reg1_rd;
wire   [3:0]    interrupt_id;

assign wr_strobe = chipselect && ~write_n;
assign rd_strobe = chipselect && ~read_n;

assign vuart_avl_fsr = {8'd0,vuart_thr_fifo_usedw,8'd0,vuart_rbr_fifo_usedw};

assign reg0_rd = dlab ? vuart_avl_dll : vuart_avl_rbr;
assign reg1_rd = dlab ? vuart_avl_dlh : vuart_avl_ier;

// Latch Address
reg [4:0] address_reg = 5'h0;
always @(posedge clk)
begin
    if (~reset_n)
         address_reg <= 5'b0;
    else if (rd_strobe)
         address_reg <= address;
end

always @(posedge clk)
begin
    if (~reset_n)
         dlab <= 1'b0;
    else if (wr_strobe && (address==5'd3))
         dlab <= writedata[7];
end

always @(posedge clk)
begin
      if (wr_strobe && (address==5'h0) && (dlab==1'b0) )
          vuart_avl_thr_en <= 1'b1;
      else
          vuart_avl_thr_en <= 1'b0;
end

always @(posedge clk)
begin
     if (rd_strobe && (address==5'h0) && (dlab==1'b0) )
         vuart_avl_rbr_en <= 1'b1;
     else
         vuart_avl_rbr_en <= 1'b0;
end


always @(posedge clk)
begin
    if (~reset_n)
    begin
        vuart_avl_thr <= 8'd0;
        vuart_avl_scr <= 8'd0;
        vuart_avl_addr <= 16'd0;
    end
    else if (wr_strobe)
    begin
        case (address)
        5'd0:    begin
                 if (~dlab) vuart_avl_thr <= writedata[7:0];
                 end
        5'd7:    vuart_avl_scr <= writedata[7:0];
        5'd8:    vuart_avl_addr <= writedata[15:0];
        endcase
    end
end
// pipeline to match fifo latency
reg [3:0] rd_sr = 4'b0;
always @(posedge clk )
 rd_sr <= {rd_sr[2:0], rd_strobe};

assign readdatavalid = rd_sr[3];

always @(posedge clk )
begin
    if (~reset_n)
      readdata <= 32'd0;
    else if (rd_sr[2])
       case (address_reg)
       5'd0:    readdata[31:0] <= {24'd0,reg0_rd};
       5'd1:    readdata[31:0] <= {24'd0,reg1_rd};
       5'd2:    readdata[31:0] <= {24'd0,vuart_avl_iir};
       5'd3:    readdata[31:0] <= {24'd0,vuart_avl_lcr};
       5'd4:    readdata[31:0] <= {24'd0,vuart_avl_mcr};
       5'd5:    readdata[31:0] <= {24'd0,vuart_avl_lsr};
       5'd6:    readdata[31:0] <= {24'd0,vuart_avl_msr};
       5'd7:    readdata[31:0] <= {24'd0,vuart_avl_scr};
       5'd8:    readdata[31:0] <= {16'd0,vuart_avl_addr};
       5'd9:    readdata[31:0] <= vuart_avl_fsr;
       5'd10:   readdata[31:0] <= isr;
       default: readdata[31:0] <= 32'd0;
       endcase
end

// Interrupt Status Register
// upper 8 bits are the msix index
// bit 0 
//  set when serial port fifo is not empty
//  cleared on read of isr
always @(posedge clk)
  if (~reset_n) 
  begin
    isr[31:24]  <= ESPI_SERIAL_MSIX_INDEX;
    isr[23:00]  <= 24'b0;
  end 
  else if (isr[0] == 1'b1) 
    begin
      if (wr_strobe && (address==5'd10) )
        isr[0]  <= 1'b0;
    end
  else if (espi_vuart_thrfifo_empty_n)
    begin
      isr[0]  <= 1'b1;
    end

// MSI-X Request/ack
//  set when serial port fifo is not empty
//  cleared on MSIX-ACK
always @(posedge clk)
  if (~reset_n) 
    espi_serial_msix_req  <= 1'b0;
  else if (~isr[0] & espi_vuart_thrfifo_empty_n)
    espi_serial_msix_req  <= 1'b1;
  else if (espi_serial_msix_ack == 1'b1)
    espi_serial_msix_req  <= 1'b0;

endmodule
